package tests;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import jeconbond.automata.*;
import jeconbond.economic.resources.IResourceType;
import jeconbond.economic.resources.ResourceBasket;
import jeconbond.economic.resources.ResourceStorrage;
import jeconbond.economic.resources.ResourceUtils;
import jeconbond.experiment.computation.DiagramUtils;


import junit.framework.Assert;
import junit.framework.TestCase;
import laboratoryQ.BaseUtils;
import laboratoryQ.computation.Random;
import laboratoryQ.computation.IRandom;

public class TestComputation extends TestCase {
	public void testRandom() {
		IRandom r = new Random();
		double d = r.get();
		Assert.assertTrue(-0.1 < d);
		Assert.assertTrue(1.1 > d);
		int f0 = 0;
		int f99 = 0;
		for (int i = 0; i < 10000; i++) {
			int r100 = BaseUtils.randomInt(r, 100);
			Assert.assertTrue(r100 >= 0);
			Assert.assertTrue(r100 < 100);
			if (r100 == 0) {
				f0++;
			}
			if (r100 == 99) {
				f99++;
			}
		}
		Assert.assertTrue(f0 > 0);
		Assert.assertTrue(f99 > 0);
	}
	
	public void testDiagramUtils() {
		List<Double> track = new ArrayList<Double>();
		track.add(Double.NaN);
		track.add(Double.POSITIVE_INFINITY);
		track.add(Double.NEGATIVE_INFINITY);
		track.add(1.0);
		track.add(Double.NaN);
		track.add(2.0);
		track.add(Double.NaN);
		double[] actual = DiagramUtils.spyTrack2diagram(track);
		double[] expected = new double[] { 
				1.0, 
				1.0, 
				1.0, 
				1.0, 
				1.0, 
				2.0, 
				2.0, 
				};
		for (int i = 0; i < actual.length; i++) {
			Assert.assertEquals(expected[i], actual[i]);
		}
	}
	
	public void testDFSCosts() {
		INode root = new Node("root"); 
		INode n0 = new Node("n0"); 
		INode n1 = new Node("n1"); 
		INode n11 = new Node("n11"); 
		INode n12 = new Node("n12"); 
		ResourceStorrage costs = new ResourceStorrage();
		ResourceStorrage profit = new ResourceStorrage();
		IResourceType[] r = ResourceUtils.generateResources("r", 6); 
		IOutcome out = null;
		
		out = new Outcome(n0, new ResourceBasket(r[0], 1.0));
		root.getOutcomes().add(out);
		out = new Outcome(root);
		out.setProducedResources(new ResourceBasket(r[1], 1.0));
		n0.getOutcomes().add(out);
		
		out = new Outcome(n1, new ResourceBasket(r[0], 10.0));
		out.setProducedResources(new ResourceBasket(r[1], 10.0));
		root.getOutcomes().add(out);
		
		out = new Outcome(n11, new ResourceBasket(r[2], 1.0));
		out.setProducedResources(new ResourceBasket(r[2], 10.0));
		n1.getOutcomes().add(out);
		
		out = new Outcome(n12, new ResourceBasket(r[3], 1.0));
		out.setProducedResources(new ResourceBasket(r[3], 10.0));
		n1.getOutcomes().add(out);
		
		out = new Outcome(root, new ResourceBasket(r[4], 1.0));
		out.setProducedResources(new ResourceBasket(r[4], 10.0));
		n11.getOutcomes().add(out);
		
		out = new Outcome(root, new ResourceBasket(r[5], 1.0));
		out.setProducedResources(new ResourceBasket(r[5], 10.0));
		n12.getOutcomes().add(out);
		
		AutomataUtils.dfsCosts(
				root, 
				costs,
				profit
		);
		
		Assert.assertEquals(21.0, costs.howMuchWasStored(r[0]));
		Assert.assertEquals(0.0, profit.howMuchWasStored(r[0]));
		Assert.assertEquals(0.0, costs.howMuchWasStored(r[1]));
		Assert.assertEquals(21.0, profit.howMuchWasStored(r[1]));
		
		Assert.assertEquals(1.0, costs.howMuchWasStored(r[2]));
		Assert.assertEquals(10.0, profit.howMuchWasStored(r[2]));
		
		Assert.assertEquals(1.0, costs.howMuchWasStored(r[3]));
		Assert.assertEquals(10.0, profit.howMuchWasStored(r[3]));
		
		Assert.assertEquals(1.0, costs.howMuchWasStored(r[4]));
		Assert.assertEquals(10.0, profit.howMuchWasStored(r[4]));
		
		Assert.assertEquals(1.0, costs.howMuchWasStored(r[5]));
		Assert.assertEquals(10.0, profit.howMuchWasStored(r[5]));
	}
	
	public void testDFSPerspectiveForksCount() {
		INode root = new Node("root");
		INode n1 = new Node("n1");
		INode n11 = new Node("n11");
		INode n2 = new Node("n2");
		INode n21 = new Node("n21");
		INode n22 = new Node("n22");
		
		root.getOutcomes().add(new Outcome(n1));
		root.getOutcomes().add(new Outcome(n2));
		
		n1.getOutcomes().add(new Outcome(n11));
		n11.getOutcomes().add(new Outcome(root));
		
		n2.getOutcomes().add(new Outcome(n21));
		n2.getOutcomes().add(new Outcome(n22));
		n21.getOutcomes().add(new Outcome(root));
		n22.getOutcomes().add(new Outcome(root));
		
		Map<INode, Integer> theMap = AutomataUtils.dfsPerspectiveForksCount(root);
		Assert.assertEquals(3, theMap.get(root).intValue());
		Assert.assertEquals(1, theMap.get(n1).intValue());
		Assert.assertEquals(1, theMap.get(n11).intValue());
		Assert.assertEquals(2, theMap.get(n2).intValue());
		Assert.assertEquals(1, theMap.get(n21).intValue());
		Assert.assertEquals(1, theMap.get(n22).intValue());
		
		INode n23 = new Node("n23");
		n23.getOutcomes().add(new Outcome(root));
		n2.getOutcomes().add(new Outcome(n23)); // change nothing
		
		theMap = AutomataUtils.dfsPerspectiveForksCount(root);
		Assert.assertEquals(4, theMap.get(root).intValue());
		Assert.assertEquals(1, theMap.get(n1).intValue());
		Assert.assertEquals(1, theMap.get(n11).intValue());
		Assert.assertEquals(3, theMap.get(n2).intValue());
		Assert.assertEquals(1, theMap.get(n21).intValue());
		Assert.assertEquals(1, theMap.get(n22).intValue());
	}
	
	public void testDFSMultiplyProfit() {
		INode root = new Node("root"); 
		INode n0 = new Node("n0"); 
		INode n1 = new Node("n1"); 
		INode n11 = new Node("n11"); 
		INode n12 = new Node("n12");
		IResourceType[] r = ResourceUtils.generateResources("r", 6);
		IOutcome out = null;
		
		out = new Outcome(n0, new ResourceBasket(r[0], 1.0));
		root.getOutcomes().add(out);
		out = new Outcome(root);
		out.setProducedResources(new ResourceBasket(r[1], 1.0));
		n0.getOutcomes().add(out);
		
		out = new Outcome(n1, new ResourceBasket(r[1], 10.0));
		out.setProducedResources(new ResourceBasket(r[2], 10.0));
		root.getOutcomes().add(out);
		
		out = new Outcome(n11, new ResourceBasket(r[2], 1.0));
		out.setProducedResources(new ResourceBasket(r[2], 10.0));
		n1.getOutcomes().add(out);
		
		out = new Outcome(n12, new ResourceBasket(r[3], 1.0));
		out.setProducedResources(new ResourceBasket(r[3], 10.0));
		n1.getOutcomes().add(out);
		
		out = new Outcome(root, new ResourceBasket(r[4], 1.0));
		out.setProducedResources(new ResourceBasket(r[4], 10.0));
		n11.getOutcomes().add(out);
		
		out = new Outcome(root, new ResourceBasket(r[5], 1.0));
		out.setProducedResources(new ResourceBasket(r[5], 10.0));
		n12.getOutcomes().add(out);
		
		Map<IResourceType, Double> mults = new HashMap<IResourceType, Double>();
		mults.put(r[1], 3.0);
		mults.put(r[2], 2.5);
		mults.put(r[5], 0.0123);
		AutomataUtils.dfsMultiplyProfit(
				root, 
				mults
		);
		
		Assert.assertEquals("r1", n0.getOutcomes().get(0).getProducedResources()[0].type.getName());
		Assert.assertEquals(3.0, n0.getOutcomes().get(0).getProducedResources()[0].value);
		Assert.assertEquals("r2", root.getOutcomes().get(1).getProducedResources()[0].type.getName());
		Assert.assertEquals(25.0, root.getOutcomes().get(1).getProducedResources()[0].value);
		Assert.assertEquals("r2", n1.getOutcomes().get(0).getProducedResources()[0].type.getName());
		Assert.assertEquals(25.0, n1.getOutcomes().get(0).getProducedResources()[0].value);
		Assert.assertEquals("r3", n1.getOutcomes().get(1).getProducedResources()[0].type.getName());
		Assert.assertEquals(10.0, n1.getOutcomes().get(1).getProducedResources()[0].value);
		Assert.assertEquals("r5", n12.getOutcomes().get(0).getProducedResources()[0].type.getName());
		Assert.assertEquals(0.123, n12.getOutcomes().get(0).getProducedResources()[0].value);
	}
	
	public void testDFSGetAllOutcomes() {
		List<IOutcome> goal = new ArrayList<IOutcome>();
		INode root = new Node("root"); 
		INode n0 = new Node("n0"); 
		INode n1 = new Node("n1"); 
		INode n11 = new Node("n11"); 
		INode n12 = new Node("n12"); 
		IResourceType[] r = ResourceUtils.generateResources("r", 6);
		IOutcome out = null;
		
		out = new Outcome(n0, new ResourceBasket(r[0], 1.0));
		goal.add(out);
		root.getOutcomes().add(out);
		out = new Outcome(root);
		goal.add(out);
		out.setProducedResources(new ResourceBasket(r[1], 1.0));
		n0.getOutcomes().add(out);
		
		out = new Outcome(n1, new ResourceBasket(r[1], 10.0));
		goal.add(out);
		out.setProducedResources(new ResourceBasket(r[2], 10.0));
		root.getOutcomes().add(out);
		
		out = new Outcome(n11, new ResourceBasket(r[2], 1.0));
		goal.add(out);
		out.setProducedResources(new ResourceBasket(r[2], 10.0));
		n1.getOutcomes().add(out);
		
		out = new Outcome(n12, new ResourceBasket(r[3], 1.0));
		goal.add(out);
		out.setProducedResources(new ResourceBasket(r[3], 10.0));
		n1.getOutcomes().add(out);
		
		out = new Outcome(root, new ResourceBasket(r[4], 1.0));
		goal.add(out);
		out.setProducedResources(new ResourceBasket(r[4], 10.0));
		n11.getOutcomes().add(out);
		
		out = new Outcome(root, new ResourceBasket(r[5], 1.0));
		goal.add(out);
		out.setProducedResources(new ResourceBasket(r[5], 10.0));
		n12.getOutcomes().add(out);
		
		Map<IResourceType, Double> mults = new HashMap<IResourceType, Double>();
		mults.put(r[1], 3.0);
		mults.put(r[2], 2.5);
		mults.put(r[5], 0.0123);
		List<IOutcome> ao = AutomataUtils.dfsGetAllOutcomes(root);
		Assert.assertEquals(goal.size(), ao.size());
		Comparator<IOutcome> cmp = new Comparator<IOutcome>() {
			public int compare(IOutcome a, IOutcome b) {
				return a.hashCode() - b.hashCode();
			}}; 
		Collections.sort(goal, cmp); 
		Collections.sort(ao, cmp);
		for (int i = 0; i < goal.size(); i++) {
			Assert.assertEquals(goal.get(i), ao.get(i));
		}
	}
}
